package com.tc.activiti.web.form.formkey;

import cn.hutool.core.collection.CollUtil;
import com.tc.activiti.util.BeanUtils;
import com.tc.activiti.util.Page;
import com.tc.activiti.util.PageUtil;
import com.tc.activiti.util.user.UserUtil;
import com.tc.base.constants.PageConstants;
import com.tc.base.constants.StateEnum;
import com.tc.base.entity.APIRequest;
import com.tc.base.entity.APIResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.persistence.entity.SuspensionState;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.NativeTaskQuery;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.Map.Entry;

/**
 * 外置表单Controller
 */
@Api(value = "外置表单流程管理器", tags = "外置表单流程管理器")
@Controller
@RequestMapping(value = "/bpmn/form/formkey")
public class FormKeyController2 {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FormService formService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ManagementService managementService;

    /**
     * 外置form流程列表
     *
     * @param request
     * @return
     */
    @ApiOperation(value = "读取外置的流程列表", notes = "读取外置的流程列表")
    @RequestMapping(value = "/processlist", method = RequestMethod.POST)
    @ResponseBody
    public APIResult processDefinitionList(@RequestBody APIRequest request) {
        /*
         * 只读取外置表单：leave-formkey，如果放在一起，是全部的表单，不进行区分，读取了外置表单增加过滤条件：leave-formkey
         */
        APIResult result = new APIResult<>();
        Page<Map<String, Object>> page = new Page<>(PageUtil.PAGE_SIZE);
        try {
            Integer pageSize = request.getRequestPage().getLimit();
            Integer pageNo = request.getRequestPage().getPageNo();
            if (pageNo == null) pageNo = PageConstants.DEFAULT_PAGE_NO;
            if (pageSize == null) pageSize = PageConstants.DEFAULT_PAGE_SIZE;
            int[] pageParams = PageUtil.init(pageNo, pageSize);
            ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave-formkey").active().orderByDeploymentId().desc();
            List<ProcessDefinition> list = query.listPage(pageParams[0], pageParams[1]);
            result.setData(list);
            result.setMessage("查询流程定义列表成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/processlist", e);
        }
        return result;
    }

    /**
     * 初始化启动流程，读取启动流程的表单内容来渲染start form
     */
    @ApiOperation(value = "读取启动流程的表单", notes = "读取启动流程的表单")
    @RequestMapping(value = "/get-form/start/{processDefinitionId}", method = RequestMethod.GET)
    @ResponseBody
    public APIResult findStartForm(@PathVariable("processDefinitionId") String processDefinitionId)  {
        APIResult result = new APIResult<>();
        try {
            // 根据流程定义ID读取外置表单
            Object startForm = formService.getRenderedStartForm(processDefinitionId);
            result.setData(startForm);
            result.setMessage("读取表单内容成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/get-form/task/" + processDefinitionId, e);
        }
        return result;
    }

    /**
     * 读取Task的表单，根据任务id读取任务处理的表单内容
     */
    @ApiOperation(value = "读取Task的表单", notes = "读取Task的表单")
    @RequestMapping(value = "/get-form/task/{taskId}", method = RequestMethod.POST)
    @ResponseBody
    public APIResult findTaskForm(@PathVariable("taskId") String taskId)  {
        APIResult result = new APIResult<>();
        try {
            Object renderedTaskForm = formService.getRenderedTaskForm(taskId);
            result.setData(renderedTaskForm);
            result.setMessage("读取表单内容成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/get-form/task/" + taskId, e);
        }
        return result;
    }

    /**
     * 办理任务，提交task的并保存form
     */
    @ApiOperation(value = "办理任务", notes = "办理任务")
    @RequestMapping(value = "/task/complete/{taskId}", method = RequestMethod.POST)
    @ResponseBody
    public APIResult completeTask(@PathVariable("taskId") String taskId, HttpServletRequest request) {
        APIResult result = new APIResult<>();
        Map<String, String> formProperties = new HashMap<String, String>();
        try {
            // 从request中读取参数然后转换
            Map<String, String[]> parameterMap = request.getParameterMap();
            Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
            for (Entry<String, String[]> entry : entrySet) {
                String key = entry.getKey();
                /*参数结构：fq_reason，用_分割 fp的意思是form paremeter 最后一个是属性名称*/
                if (StringUtils.defaultString(key).startsWith("fp_")) {
                    String[] paramSplit = key.split("_");
                    formProperties.put(paramSplit[1], entry.getValue()[0]);
                }
            }
            logger.debug("start form parameters: {}", formProperties);
            //session获取用户信息
            User user = UserUtil.getUserFromSession(request.getSession());
            // 用户未登录不能操作，实际应用使用权限框架实现，例如Spring Security、Shiro等
            ProcessInstance processInstance = null;
            if (user == null || StringUtils.isBlank(user.getId())) {
                throw new RuntimeException("用户未登录，不能操作");
            } else {
                identityService.setAuthenticatedUserId(user.getId());
                formService.submitTaskFormData(taskId, formProperties);
                logger.debug("start a processinstance: {}", processInstance);
                result.setMessage("任务完成：taskId=" + taskId);
                result.setState(200);
            }
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/task/complete/" + taskId, e);
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
        return result;
    }

    /**
     * 读取启动流程的表单字段
     */
    @ApiOperation(value = "提交启动流程", notes = "提交启动流程")
    @RequestMapping(value = "/start-process/{processDefinitionId}", method = RequestMethod.POST)
    @ResponseBody
    public APIResult submitStartFormAndStartProcessInstance(@PathVariable("processDefinitionId") String processDefinitionId,
                                                            HttpServletRequest request) {
        APIResult result = new APIResult<>();
        try {
            Map<String, String> formProperties = new HashMap<String, String>();
            // 从request中读取参数然后转换
            Map<String, String[]> parameterMap = request.getParameterMap();
            Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
            for (Entry<String, String[]> entry : entrySet) {
                String key = entry.getKey();
                // fp_的意思是form paremeter
                if (StringUtils.defaultString(key).startsWith("fp_")) {
                    formProperties.put(key.split("_")[1], entry.getValue()[0]);
                }
            }
            logger.debug("start form parameters: {}", formProperties);
            User user = UserUtil.getUserFromSession(request.getSession());
            // 用户未登录不能操作，实际应用使用权限框架实现，例如Spring Security、Shiro等
            ProcessInstance processInstance = null;
            if (user == null || StringUtils.isBlank(user.getId())) {
                throw new RuntimeException("用户未登录，不能操作");
            } else {
                identityService.setAuthenticatedUserId(user.getId());
                processInstance = formService.submitStartFormData(processDefinitionId, formProperties);
                logger.debug("start a processinstance: {}", processInstance);
                result.setMessage("启动成功，流程ID：" + processInstance.getId());
                result.setState(200);
            }
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/start-process/" + processDefinitionId, e);
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
        return result;
    }


    /**
     * task列表
     *
     * @param request
     * @param servletRequest
     * @return
     */
    @ApiOperation(value = "获取获选人待办任务列表", notes = "获取获选人待办任务列表")
    @RequestMapping(value = "/task/list", method = RequestMethod.POST)
    @ResponseBody
    public APIResult taskList(@RequestBody(required = true) APIRequest request,
                              HttpServletRequest servletRequest) {
        APIResult<Page> result = new APIResult<>();
        Page<Task> page = null;
        try {
            User user = UserUtil.getUserFromSession(servletRequest.getSession());
            page = new Page<Task>(PageUtil.PAGE_SIZE);
            Integer pageSize = request.getRequestPage().getLimit();
            Integer pageNo = request.getRequestPage().getPageNo();
            if (pageNo == null) pageNo = PageConstants.DEFAULT_PAGE_NO;
            if (pageSize == null) pageSize = PageConstants.DEFAULT_PAGE_SIZE;
            int[] pageParams = PageUtil.init(pageNo, pageSize);
            /**
             * 这里为了演示区分开自定义表单的请假流程，值读取leave-formkey
             */
            // 已经签收的或者直接分配到当前人的任务
            String asigneeSql = "select distinct RES.* from ACT_RU_TASK RES inner join ACT_RE_PROCDEF D on RES.PROC_DEF_ID_ = D.ID_ WHERE RES.ASSIGNEE_ = #{userId}"
                    + " and D.KEY_ = #{processDefinitionKey} and RES.SUSPENSION_STATE_ = #{suspensionState}";

            // 当前人在候选人或者候选组范围之内
            String needClaimSql = "select distinct RES1.* from ACT_RU_TASK RES1 inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES1.ID_ inner join ACT_RE_PROCDEF D1 on RES1.PROC_DEF_ID_ = D1.ID_ WHERE"
                    + " D1.KEY_ = #{processDefinitionKey} and RES1.ASSIGNEE_ is null and I.TYPE_ = 'candidate'"
                    + " and ( I.USER_ID_ = #{userId} or I.GROUP_ID_ IN (select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = #{userId} ) )"
                    + " and RES1.SUSPENSION_STATE_ = #{suspensionState}";
            String sql = asigneeSql + " union all " + needClaimSql;
            NativeTaskQuery query = taskService.createNativeTaskQuery().sql(sql)
                    .parameter("processDefinitionKey", "leave-formkey").parameter("suspensionState", SuspensionState.ACTIVE.getStateCode())
                    .parameter("userId", user.getId());
            List<Task> tasks = query.listPage(pageParams[0], pageParams[1]);

            page.setTotalCount(query.sql("select count(*) from (" + sql + ") as CT").count());
            page.setResult(tasks);
            result.setData(page);
            result.setData(page);
            result.setMessage("获取获选人待办任务列表成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey//task/list", e);
        }
        return result;
    }

    /**
     * 签收任务
     */
    @ApiOperation(value = "签收任务", notes = "签收任务")
    @RequestMapping(value = "/task/claim/{id}", method = RequestMethod.GET)
    @ResponseBody
    public APIResult claim(@PathVariable("id") String taskId, HttpSession session) {
        APIResult result = new APIResult<>();
        try {
            String userId = UserUtil.getUserFromSession(session).getId();
            if (BeanUtils.isEmpty(userId)) {
                throw new RuntimeException("请先登录再操作");
            }
            taskService.claim(taskId, userId);
            result.setMessage("签收任务成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/task/claim/" + taskId, e);
        }
        return result;
    }


    /**
     * 运行中的流程实例
     *
     * @param request
     * @return
     */
    @ApiOperation(value = "运行中的流程实例", notes = "运行中的流程实例")
    @RequestMapping(value = "/process-instance/running/list", method = RequestMethod.POST)
    @ResponseBody
    public APIResult running(@RequestBody(required = true) APIRequest request) {
        APIResult<Page> result = new APIResult<>();
        Page page = new Page<>(PageUtil.PAGE_SIZE);
        try {
            Integer pageSize = request.getRequestPage().getLimit();
            Integer pageNo = request.getRequestPage().getPageNo();
            if (pageNo == null) pageNo = PageConstants.DEFAULT_PAGE_NO;
            if (pageSize == null) pageSize = PageConstants.DEFAULT_PAGE_SIZE;
            int[] pageParams = PageUtil.init(pageNo, pageSize);
            ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery().orderByProcessInstanceId().desc().active();
            List<ProcessInstance> list = query.listPage(pageParams[0], pageParams[1]);
            page.setTotalCount(query.count());
            page.setResult(list);
            result.setData(page);
            result.setMessage("查询运行中的流程成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/process-instance/running/list", e);
        }
        return result;
    }


    /**
     * 已结束的流程实例
     *
     * @param request
     * @return
     */
    @ApiOperation(value = "已结束的流程", notes = "已结束的流程")
    @RequestMapping(value = "/process-instance/finished/list", method = RequestMethod.POST)
    @ResponseBody
    public APIResult finished(@RequestBody(required = true) APIRequest request) {
        APIResult<Page> result = new APIResult<>();
        List<Map<String, Object>> listMap = new ArrayList<>();
        Page<Map<String, Object>> page = new Page<>(PageUtil.PAGE_SIZE);
        try {
            Integer pageSize = request.getRequestPage().getLimit();
            Integer pageNo = request.getRequestPage().getPageNo();
            if (pageNo == null) pageNo = PageConstants.DEFAULT_PAGE_NO;
            if (pageSize == null) pageSize = PageConstants.DEFAULT_PAGE_SIZE;
            int[] pageParams = PageUtil.init(pageNo, pageSize);
            HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery().finished().orderByProcessInstanceEndTime().desc();
            List<HistoricProcessInstance> list = query.listPage(pageParams[0], pageParams[1]);
            if (CollUtil.isNotEmpty(list)) {
                for (HistoricProcessInstance historicProcessInstance : list) {
                    Map<String, Object> values = new HashMap<String, Object>();
                    values.put("id", historicProcessInstance.getId());
                    values.put("name", historicProcessInstance.getName());
                    values.put("businessKey", historicProcessInstance.getBusinessKey());
                    values.put("deploymentId", historicProcessInstance.getDeploymentId());
                    values.put("description", historicProcessInstance.getDescription());
                    values.put("processDefinitionId", historicProcessInstance.getProcessDefinitionId());
                    values.put("processDefinitionName", historicProcessInstance.getProcessDefinitionName());
                    values.put("processDefinitionVersion", historicProcessInstance.getProcessDefinitionVersion());
                    listMap.add(values);
                }
            }
            page.setTotalCount(query.count());
            page.setResult(listMap);
            result.setData(page);
            result.setMessage("查询运行结束的流程成功");
        } catch (Exception e) {
            result.setState(StateEnum.ERROR_BPMN.getCode());
            result.setMessage(StateEnum.ERROR_BPMN.getText());
            result.setCause(e.getMessage());
            logger.error("/bpmn/form/formkey/process-instance/finished/list", e);
        }
        return result;
    }

}
